/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_compressor.h"
#include "umc_avs_slice.h"
#include "umc_avs_pic.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_enc_tables.h"
#include "umc_avs_enc_vlc.h"

#include "umc_avs_enc_bit_stream.h"

namespace UMC
{

void AVSCompressor::CompressSliceHeader(void)
{
    // encode slice header
    if (MbIndex == m_encCtx.MbFirst)
    {
        AVSSlice *pSlice = m_encCtx.m_pSlice;

        // update slice header
        if (0 == m_encCtx.FixedQP)
        {
            pSlice->m_SlcHeader.slice_qp = m_pMbInfo->QP;
            m_encCtx.PreviousQP = m_pMbInfo->QP;
        }

        // put it into a bit stream
        EncodeSlcHeader(&m_encCtx.m_stream,
                        m_encCtx.m_pSeqHeader,
                        m_encCtx.m_pPicHeader,
                        &(pSlice->m_SlcHeader));
    }
}// void AVSCompressor::CompressSliceHeader(void)

void AVSCompressor::CompressIMacroBlocksRow(void)
{
    MbMax = m_encCtx.MbIndex + m_encCtx.MbWidth;

    // we can use MbIndex either from decoding and reconstructing contexts
    MbIndex = m_encCtx.MbIndex;

    PrepareNeighbours(&m_encCtx);
    PrepareDisassembling();
    PrepareDecoding();
    PrepareReconstruction();
    CompressSliceHeader();

    do
    {
        if (0 == MbIndex)
        {
            WRITE_TRACE_LOG(_enc_tst, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_rec_tst_enc, "Frame num is  ", m_encCtx.m_pPicHeader->picture_distance - 1);
        }
        WRITE_TRACE_LOG_MB_TITLE(_dis_tst, MbIndex, m_disCtx.MbWidth, m_disCtx.MbHeight, 0 == m_disCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_enc_tst, MbIndex, m_encCtx.MbWidth, m_encCtx.MbHeight, 0 == m_encCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_rec_tst_enc, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);

        // set the macro block info into the initial state
        ResetMacroblock();

        // reset coefficient pointers
        m_pWriteCoeffs = (Ipp16s *)m_disCtx.m_pCoeffs;
        m_pReadCoeffs = m_encCtx.m_pCoeffs;

        // disassemble MB into parts
        DisassembleIMacroBlock();

        // encode MB into a bit stream
        EncodeIMacroBlock();

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDisassembling();
        AdvanceEncoding();
        AdvanceReconstruction();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDisassembling();
    FinalizeEncoding();
    FinalizeReconstruction();

} // void AVSCompressor::CompressIMacroBlocksRow(void)

void AVSCompressor::EncodeIMacroBlock(void)
{
    // encode prediction modes
    {
        AVS_MB_INFO *pLeft = m_pMbInfoLeft, *pTop = m_pMbInfoTop;

        // we can predict modes only from I macroblock(s)
        if ((pLeft) &&
            (I_8x8 != pLeft->MbType))
            pLeft = NULL;
        if ((pTop) &&
            (I_8x8 != pTop->MbType))
            pTop = NULL;

        EncodeIntraPredModeISlice(pLeft, pTop, 0);
        EncodeIntraPredModeISlice(m_pMbInfo, pTop, 1);
        EncodeIntraPredModeISlice(pLeft, m_pMbInfo, 2);
        EncodeIntraPredModeISlice(m_pMbInfo, m_pMbInfo, 3);
    }

    WRITE_TRACE_LOG(_enc_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[0]);
    WRITE_TRACE_LOG(_enc_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[1]);
    WRITE_TRACE_LOG(_enc_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[2]);
    WRITE_TRACE_LOG(_enc_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[3]);

    // encode chrominance prediction mode(s)
    PutUE(&m_encCtx.m_stream, (Ipp32u) m_pMbInfo->IntraChromaPredMode[0]);
    WRITE_TRACE_LOG(_enc_tst, "intra chroma pred mode 0", m_pMbInfo->IntraChromaPredMode[0]);
    if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
    {
        PutUE(&m_encCtx.m_stream, m_pMbInfo->IntraChromaPredMode[1]);
        WRITE_TRACE_LOG(_enc_tst, "intra chroma pred mode 1", m_pMbInfo->IntraChromaPredMode[1]);
    }

    // encode coded block pattern
    if (AVS_I_PICTURE == m_encCtx.m_pPicHeader->PictureType)
        //picHeader.picture_start_code = I_PICTURE_START_CODE
    {
        Ipp32u uTemp;

        // convert CBP to code
        uTemp = CBPOfMacroblock2Code[AVS_INTRA][m_pMbInfo->MbCBP & 0x3f];

        // save it to the bit stream
        PutUE(&m_encCtx.m_stream, uTemp);
        WRITE_TRACE_LOG(_enc_tst, "CBP code", uTemp);
        // save additional chroma bits
        if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
        {
            uTemp = (m_pMbInfo->MbCBP >> 6) & 0x3;

            PutUE(&m_encCtx.m_stream, uTemp);
            WRITE_TRACE_LOG(_enc_tst, "CBP code +", uTemp);
        }
    }

    // encode quantization parameter
    if ((m_pMbInfo->MbCBP) &&
        (0 == m_encCtx.FixedQP))
    {
        Ipp32s mb_qp_delta;

        // get QP delta
        mb_qp_delta = m_pMbInfo->QP - m_encCtx.PreviousQP;

        // save delta to the bit stream
        PutSE(&m_encCtx.m_stream, mb_qp_delta);
        WRITE_TRACE_LOG(_enc_tst, "mb qp delta", mb_qp_delta);

        // update previous QP
        m_encCtx.PreviousQP = m_pMbInfo->QP;
    }

    // encode blocks coefficients
    {
        Ipp32u i;
        Ipp32u uBlockMask = 1;

        // decode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                avs_EncodeLumaBlockIntra_AVS_1u16s(&m_encCtx.m_stream.dst,
                                                   &m_encCtx.m_stream.offset,
                                                   m_pReadCoeffs,
                                                   m_encCtx.ScanType);
                m_pReadCoeffs += 64;
            }
        }

        // encode chrominance blocks
        for (; i < m_encCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                avs_EncodeChromaBlock_AVS_1u16s(&m_encCtx.m_stream.dst,
                                                &m_encCtx.m_stream.offset,
                                                m_pReadCoeffs,
                                                m_encCtx.ScanType);
                m_pReadCoeffs += 64;
            }
        }
    }

} // void AVSCompressor::EncodeIMacroBlock(void)

Ipp32u AVSCompressor::GetSizeIMacroBlock(void)
{
    Ipp32u requiredSize = 0;

    // update slice header
    if (MbIndex == m_encCtx.MbFirst)
    {
        // update slice header
        if (0 == m_encCtx.FixedQP)
        {
            m_encCtx.PreviousQP = m_pMbInfo->QP;
        }
    }

    // encode prediction modes
    {
        AVS_MB_INFO *pLeft = m_pMbInfoLeft, *pTop = m_pMbInfoTop;

        // we can predict modes only from I macroblock(s)
        if ((pLeft) &&
            (I_8x8 != pLeft->MbType))
            pLeft = NULL;
        if ((pTop) &&
            (I_8x8 != pTop->MbType))
            pTop = NULL;

        requiredSize += GetSizeIntraPredModeISlice(pLeft, pTop, 0);
        requiredSize += GetSizeIntraPredModeISlice(m_pMbInfo, pTop, 1);
        requiredSize += GetSizeIntraPredModeISlice(pLeft, m_pMbInfo, 2);
        requiredSize += GetSizeIntraPredModeISlice(m_pMbInfo, m_pMbInfo, 3);
    }

    // encode chrominance prediction mode(s)
    requiredSize += GetUESize((Ipp32u) m_pMbInfo->IntraChromaPredMode[0]);
    if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
    {
        requiredSize += GetUESize(m_pMbInfo->IntraChromaPredMode[1]);
    }

    // encode coded block pattern
    {
        Ipp32u uTemp;

        // convert CBP to code
        uTemp = CBPOfMacroblock2Code[AVS_INTRA][m_pMbInfo->MbCBP & 0x3f];

        // save it to the bit stream
        requiredSize += GetUESize(uTemp);
        // save additional chroma bits
        if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
        {
            uTemp = (m_pMbInfo->MbCBP >> 6) & 0x3;

            requiredSize += GetUESize(uTemp);
        }
    }

    // encode quantization parameter
    if ((m_pMbInfo->MbCBP) &&
        (0 == m_encCtx.FixedQP))
    {
        Ipp32s mb_qp_delta;

        // get QP delta
        mb_qp_delta = m_pMbInfo->QP - m_encCtx.PreviousQP;

        // save delta to the bit stream
        requiredSize += GetSESize(mb_qp_delta);
    }

    // encode blocks coefficients
    {
        Ipp32u i;
        Ipp32u uBlockMask = 1;
        const Ipp16s *pReadCoeffs = m_pReadCoeffs;

        // decode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                requiredSize += avs_GetSizeLumaBlockIntra_AVS_1u16s(pReadCoeffs,
                                                                    m_encCtx.ScanType);
                pReadCoeffs += 64;
            }
        }

        // encode chrominance blocks
        for (; i < m_encCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                requiredSize += avs_GetSizeChromaBlock_AVS_1u16s(pReadCoeffs,
                                                                 m_encCtx.ScanType);
                pReadCoeffs += 64;
            }
        }
    }

    return requiredSize;

} // Ipp32u AVSCompressor::GetSizeIMacroBlock(void)

void AVSCompressor::FillUpMeMbStatIMacroBlock(void)
{
    // fill up MeMbStat structures of MeFrame for feedback mode of AVS_ME
    MeMbStat *curStat = m_encCtx.m_pSlice->m_pPic->m_pMeFrame->stat;

    Ipp32u i, requiredSize = 0;
    curStat[MbIndex].whole = (Ipp16u) GetSizeIMacroBlock();
    curStat[MbIndex].MbType = 0;  // ME_BlockFrw
    curStat[MbIndex].qp = (Ipp16u) m_pMbInfo->QP;
    curStat[MbIndex].MVB[0] = 0;
    curStat[MbIndex].dist = 0 ;
    // calculate size of predictions modes
    {
        AVS_MB_INFO *pLeft = m_pMbInfoLeft, *pTop = m_pMbInfoTop;

        // we can predict modes only from I macroblock(s)
        if ((pLeft) &&
            (I_8x8 != pLeft->MbType))
            pLeft = NULL;
        if ((pTop) &&
            (I_8x8 != pTop->MbType))
            pTop = NULL;

        requiredSize += GetSizeIntraPredModeISlice(pLeft, pTop, 0);
        requiredSize += GetSizeIntraPredModeISlice(m_pMbInfo, pTop, 1);
        requiredSize += GetSizeIntraPredModeISlice(pLeft, m_pMbInfo, 2);
        requiredSize += GetSizeIntraPredModeISlice(m_pMbInfo, m_pMbInfo, 3);
    }

    // encode chrominance prediction mode(s)
    requiredSize += GetUESize((Ipp32u) m_pMbInfo->IntraChromaPredMode[0]);
    if (AVS_CHROMA_422_FORMAT == m_encCtx.m_pSeqHeader->chroma_format)
    {
        requiredSize += GetUESize(m_pMbInfo->IntraChromaPredMode[1]);
    }
    curStat[MbIndex].MVF[0] = (Ipp16u) requiredSize;

    requiredSize = 0;
    Ipp32u uBlockMask = 1;
    const Ipp16s *pReadCoeffs = m_pReadCoeffs;

    // encode luminance blocks
    for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
    {
        if (m_pMbInfo->MbCBP & uBlockMask)
        {
            curStat[MbIndex].coeff[i] =(Ipp16u) avs_GetSizeLumaBlockIntra_AVS_1u16s(pReadCoeffs,
                                                                                    m_encCtx.ScanType);
            pReadCoeffs += 64;
        }
        else
            curStat[MbIndex].coeff[i] = 0;
    }

    // encode chrominance blocks
    for (; i < m_encCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
    {
        if (m_pMbInfo->MbCBP & uBlockMask)
        {
            curStat[MbIndex].coeff[i] = (Ipp16u) avs_GetSizeChromaBlock_AVS_1u16s(pReadCoeffs,
                                                                                   m_encCtx.ScanType);
            pReadCoeffs += 64;
        }
        else
            curStat[MbIndex].coeff[i] = 0;
    }
    //curStat[MbIndex].coeff[0] = (Ipp16u)requiredSize;

} // void AVSCompressor::FillUpMeMbStatIMacroBlock(void)


} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
